package com.sinaapp.gavinzhang.GSpider.http.servlet;

import com.caucho.hessian.client.HessianRuntimeException;
import com.caucho.hessian.io.SerializerFactory;
import com.caucho.hessian.server.HessianServlet;
import com.caucho.services.server.ServiceContext;
import com.sinaapp.gavinzhang.GSpider.common.FileUtil;
import com.sinaapp.gavinzhang.GSpider.common.IpUtil;
import com.sinaapp.gavinzhang.GSpider.common.RegisterUtil;
import com.sinaapp.gavinzhang.GSpider.common.ServletUtil;
import com.sinaapp.gavinzhang.GSpider.config.ConfigDispatcher;
import com.sinaapp.gavinzhang.GSpider.config.PageProcessorConfig;
import com.sinaapp.gavinzhang.GSpider.core.PageProcessor;
import com.sinaapp.gavinzhang.GSpider.http.annotation.ServletClass;
import com.sinaapp.gavinzhang.GSpider.management.Register;
import com.sinaapp.gavinzhang.GSpider.management.RegistrationCenter;
import com.sinaapp.gavinzhang.GSpider.management.Stub;
import com.sinaapp.gavinzhang.GSpider.process.ProcessorDispatcher;
import com.sinaapp.gavinzhang.GSpider.webaddress.URLDispatcher;
import com.sinaapp.gavinzhang.GSpider.webaddress.WebAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

import static com.sinaapp.gavinzhang.GSpider.common.IpUtil.getRealIp;

/**
 * @author gavin
 * @version 1.0
 * @date 2016/4/5
 * @description 配置状态管理器
 * 可以提交抓取集群配置，显示抓取集群管理器和网址管理器状态
 */
@ServletClass(requestName = "/" + RegistrationCenter.CONFIGDISPATCHER)
public class ConfigServlet extends HessianServlet implements ConfigDispatcher {
    private static final Logger logger = LoggerFactory.getLogger(ConfigServlet.class);
    private static final Logger htmlLogger = LoggerFactory.getLogger("com.sinaapp.gavinzhang.GSpider.http.servlet.ConfigServlet.html");

    public static Stub configStub = Stub.newStub();
    public static String centerIp;
    public static int centerPort;

    private PageProcessorConfig pageProcessorConfig = PageProcessorConfig.newPageProcessorConfig();

    public ConfigServlet() {
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.addHeader("Content-Type", "text/html;charset=utf-8");
        resp.addHeader("Server", "GS1.1");

        if (req.getQueryString().contains("log")) {
            lookLog(req, resp);
        } else if (req.getQueryString().contains("status")) {
            lookStatus(req, resp);
        } else if (req.getQueryString().contains("shutdown")) {
            lookShutdown(req, resp);
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.addHeader("Content-Type", "text/html;charset=utf-8");
        resp.addHeader("Server", "GS1.1");
        if (req.getParameterMap().containsKey("centerIp") && req.getParameterMap().containsKey("centerPort")) {
            flushCenterConfig(req, resp);
        } else if (req.getParameterMap().containsKey("regex")) {
            flushPageConfig(req, resp);
        }
    }

    @Override
    public void service(ServletRequest request, ServletResponse response) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;

        if (req.getMethod().equals("GET")) {
            if (req.getQueryString() != null) {
                doGet(req, res);
            }

        }

        if (!req.getMethod().equals("POST")) {
            res.setStatus(500); // , "Hessian Requires POST");
            PrintWriter out = res.getWriter();

            res.setContentType("text/html");
            out.println("<h1>Requires POST</h1>");

            return;
        }

        if (req.getParameterMap().containsKey("regex") || req.getParameterMap().containsKey("centerIp")) {
            doPost(req, res);
            return;
        }

        String serviceId = req.getPathInfo();
        String objectId = req.getParameter("id");
        if (objectId == null)
            objectId = req.getParameter("ejbid");

        ServiceContext.begin(req, res, serviceId, objectId);

        try {
            InputStream is = request.getInputStream();
            OutputStream os = response.getOutputStream();

            response.setContentType("x-application/hessian");

            SerializerFactory serializerFactory = getSerializerFactory();

            invoke(is, os, objectId, serializerFactory);
        } catch (RuntimeException e) {
            throw e;
        } catch (ServletException e) {
            throw e;
        } catch (Throwable e) {
            throw new ServletException(e);
        } finally {
            ServiceContext.end();
        }
    }

    @Override
    public PageProcessorConfig getPageProcessorConfig() {
        return pageProcessorConfig;
    }

    @Override
    public int echo() {
        return HEARTECHO;
    }

    @Override
    public void shutdown() {
        System.exit(HEARTECHO);
    }

    @Override
    public void info(String message) {
        htmlLogger.info(message);
    }

    @Override
    public void warn(String message) {
        htmlLogger.warn(message);
    }

    @Override
    public void error(String message) {
        htmlLogger.error(message);
    }

    /**
     * 查看日志文件
     *
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    private void lookLog(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        logger.info("查看日志");
        File file = new File("log/log.html");
        if (file.exists()) {
            PrintWriter writer = resp.getWriter();
            String index = FileUtil.readBytes(new FileInputStream(file), "UTF8");
            writer.write(index);
            writer.flush();
            writer.close();
        } else {
            logger.warn("日志不存在 " + file.getAbsolutePath());
            ServletUtil.getMessage(req, resp, "日志不存在");
        }
    }

    /**
     * 查看抓取集群,网址管理器状态
     *
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    private void lookStatus(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        logger.info("查看状态");
        String configHtml = "";
        //测试环境能用  打成jar包就不能用了
//            URL base = ClassLoader.getSystemResource("");
//            File file = new File(base.getPath() + "/html/status.html");
//            if (file.exists()) {
//                byte[] byteArray = FileUtil.readBytes(new FileInputStream(file));
//                configHtml = new String(byteArray, "UTF8");
//            }
        configHtml = ServletUtil.readHtml("html/status.html", "UTF8");
        //应该不可能 从httpserver启动时已经带了注册中心IP
        if (centerIp == null || centerPort == 0) {
            centerIp = IpUtil.getRealIp();
            centerPort = RegistrationCenter.DEFAULT_PORT;
        }
        Register center = RegisterUtil.getRegister(centerIp, centerPort, RegistrationCenter.REGISTRATIONCENTER);

        List<Stub> stubs = new ArrayList<>();
        try {
            stubs = center.getURLDispatchers();
        } catch (Exception e) {
            logger.error("注册中心连接超时!" + centerIp + ":" + centerPort);
            ServletUtil.getMessage(req, resp, "注册中心连接超时!" + centerIp + ":" + centerPort);
            return;
        }
        int pendingSize = 0;
        int visitedSize = 0;
        for (Stub s : stubs) {
            if (s.equals(Stub.getNoneStub())) {
                logger.warn("找不到网址管理器");
            } else {
                URLDispatcher urlDispatcher = (URLDispatcher) RegisterUtil.getHessianServer(URLDispatcher.class, s.getName());
                pendingSize += urlDispatcher.pendingSize();
                visitedSize += urlDispatcher.visitedSize();
            }
        }
        configHtml = configHtml.replace("${urlDispatcherSize}", String.valueOf(stubs.size()));
        configHtml = configHtml.replace("${pendingSize}", String.valueOf(pendingSize));
        configHtml = configHtml.replace("${visitedSize}", String.valueOf(visitedSize));

        try {
            stubs = center.getProcessorDispatchers();
        } catch (HessianRuntimeException e) {
            logger.error("注册中心连接超时!" + centerIp + ":" + centerPort);
            ServletUtil.getMessage(req, resp, "注册中心连接超时!" + centerIp + ":" + centerPort);
            return;
        }
        String status = "";
        for (Stub s : stubs) {
            if (s.equals(Stub.getNoneStub())) {
                logger.warn("找不到抓取集群管理器");
            } else {
                ProcessorDispatcher processorDispatcher = (ProcessorDispatcher) RegisterUtil.getHessianServer(ProcessorDispatcher.class, s.getName());
                status += s.getName() + processorDispatcher.getStatus() + "<br>";
            }
        }
        configHtml = configHtml.replace("${processorDispatcherSize}", String.valueOf(stubs.size()));
        configHtml = configHtml.replace("${status}", status);

        PrintWriter writer = resp.getWriter();
        writer.write(configHtml);
        writer.flush();
        writer.close();
    }

    /**
     * 关闭抓取集群 （测试）
     *
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    private void lookShutdown(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        logger.info("关闭抓取集群");
        Register center = RegisterUtil.getRegister(centerIp, centerPort, RegistrationCenter.REGISTRATIONCENTER);
        try {
            center.shutdownProcessor();
        } catch (Exception e) {
            logger.error("注册中心连接超时!" + centerIp + ":" + centerPort);
            ServletUtil.getMessage(req, resp, "注册中心连接超时!" + centerIp + ":" + centerPort);
            return;
        }
        ServletUtil.getMessage(req, resp, "关闭成功!");
    }

    /**
     * 刷新注册中心地址
     *
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    private void flushCenterConfig(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        logger.info("刷新注册中心");
        //得到注册中心
        Register center = RegisterUtil.getRegister(req.getParameterMap().get("centerIp")[0], Integer.valueOf(req.getParameterMap().get("centerPort")[0]), RegistrationCenter.REGISTRATIONCENTER);

        //注册服务
        String name = "http://" + getRealIp() + ":" + req.getServerPort() + "/" + RegistrationCenter.CONFIGDISPATCHER;
        configStub.setName(name);
        try {
            center.Registrate(configStub);
        } catch (HessianRuntimeException e) {
            logger.error("注册中心连接超时!" + req.getParameterMap().get("centerIp")[0] + ":" + req.getParameterMap().get("centerPort")[0]);
            ServletUtil.getMessage(req, resp, "注册中心连接超时!" + req.getParameterMap().get("centerIp")[0] + ":" + req.getParameterMap().get("centerPort")[0]);
            return;
        }
        centerIp = req.getParameterMap().get("centerIp")[0];
        centerPort = Integer.valueOf(req.getParameterMap().get("centerPort")[0]);
        ServletUtil.getMessage(req, resp, "注册中心设置成功" + centerIp + ":" + centerPort);
    }

    /**
     * 刷新页面处理配置
     *
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    private void flushPageConfig(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        logger.info("注册配置文件");
        if (pageProcessorConfig == null) {
            pageProcessorConfig = PageProcessorConfig.newPageProcessorConfig();
        }
        pageProcessorConfig.setWaitTime(Integer.valueOf(req.getParameterMap().get("waitTime")[0]));
        pageProcessorConfig.setTimeout(Integer.valueOf(req.getParameterMap().get("timeout")[0]));
        pageProcessorConfig.setSleepTime(Integer.valueOf(req.getParameterMap().get("sleepTime")[0]));
        String regexs = req.getParameterMap().get("regex")[0];
        if (regexs.contains(";")) {
            for (String s : regexs.split(";")) {
                pageProcessorConfig.getPatterns().add(Pattern.compile(s));
            }
        } else if (regexs.length() != 0) {
            pageProcessorConfig.getPatterns().add(Pattern.compile(regexs));
        }

        String file = req.getParameterMap().get("file")[0];
        if (!file.equals("")) {
            try {
                Class c = this.getClass().getClassLoader().loadClass(file);
                for (Class cl : c.getInterfaces()) {
                    logger.info("加载页面处理类: " + cl.getName());
                }
                pageProcessorConfig.setPageProcessor((PageProcessor) c.newInstance());

            } catch (Exception e) {
                logger.error("加载页面处理类失败!" + file, e);
                ServletUtil.getMessage(req, resp, "加载页面处理类失败!" + file + "\n" + e);
                return;
            }
        }

        try {
            Register center = RegisterUtil.getRegister(centerIp, centerPort, RegistrationCenter.REGISTRATIONCENTER);

            //注册服务 可不用
            String name = "http://" + getRealIp() + ":" + req.getServerPort() + "/" + RegistrationCenter.CONFIGDISPATCHER;
            configStub.setName(name);
            try {
                center.Registrate(configStub);
            } catch (HessianRuntimeException e) {
                logger.error("注册中心连接超时!" + centerIp + ":" + centerPort);
                ServletUtil.getMessage(req, resp, "注册中心连接超时!" + centerIp + ":" + centerPort);
                return;
            }


            //分发数据
            List<Stub> stubs = center.getURLDispatchers();
            for (Stub s : stubs) {
                URLDispatcher urlDispatcher = (URLDispatcher) RegisterUtil.getHessianServer(URLDispatcher.class, s.getName());
                urlDispatcher.add(new WebAddress(req.getParameterMap().get("initUrl")[0], 0, 0));
            }
            if (stubs.size() == 0) {
                logger.warn("找不到网址管理器");
            }

            //最新配置分发到抓取集群中
            stubs = center.getProcessorDispatchers();
            for (Stub s : stubs) {
                ProcessorDispatcher processorDispatcher = (ProcessorDispatcher) RegisterUtil.getHessianServer(ProcessorDispatcher.class, s.getName());
                processorDispatcher.setPageProcessorConfig(pageProcessorConfig);
            }
            if (stubs.size() == 0) {
                logger.warn("找不到抓取集群管理器");
            }

        } catch (Exception e) {
            logger.error("注册失败！" + centerIp + ":" + centerPort, e);
            ServletUtil.getMessage(req, resp, "设置页面处理配置失败!" + e);
            return;
        }
        IndexServlet.pageProcessorConfig = pageProcessorConfig;
        IndexServlet.trueinitUrl = req.getParameterMap().get("initUrl")[0];
        ServletUtil.getMessage(req, resp, "设置页面处理配置成功!");
    }

}
